package com.fjnu.aspect;


import com.fjnu.annotation.NPECatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
@Aspect
public class LogAspect {

    /**
     * Part one:  joinpoints  by pointcut
     */
    @Pointcut("execution(public * com.fjnu.dao.*.*(..))")
    public void allFunctions() {}



    /**
     * Part two: advices: enhanced functional
     *
     */

    @Before("allFunctions()")
    public void logStartTimeAdvice(JoinPoint jp) throws Exception {

        /**
         * Enhanced functional
         */
        String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                .format(new Date()) ;
        System.out.println("[INFO] [BeforeAdvice] the method: "
                   + jp.getSignature().getName()
                     +" was executed at " + currentTime) ;


    }

    @After("allFunctions()")
    public void logEndTimeAdvice(JoinPoint jp) throws Exception {

        /**
         * Enhanced functional
         */
        String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                .format(new Date()) ;
        System.out.println("[INFO] [After Advice] the method: "
                + jp.getSignature().getName()
                +" was end at " + currentTime) ;


    }

    @Around("allFunctions()")
    public Object performanceLogAdvice(ProceedingJoinPoint pjp) throws Throwable {

        String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

        long startTime = System.currentTimeMillis() ;
        // before it execution, enhanced functional could add here.

        Object ret = pjp.proceed() ; // - ->  target method execution .
        // after the point,  here could be added enhanced functional
        System.out.println("[INFO] [" + currentTime + "] method "
                + pjp.getSignature().getName()
                + " was executed and spend for " + (System.currentTimeMillis() - startTime)*1.0/1000
                + " second" );
        return ret ;
    }


    /**
     * 处理类级别的 @NPECatch 注解
     * 匹配所有在类上标注了 @NPECatch 的方法
     */
    @Around("@within(npeCatch)")
    public Object handleClassLevelNPECatch(ProceedingJoinPoint joinPoint, NPECatch npeCatch) throws Throwable {
        return handleNPECatch(joinPoint, npeCatch, "CLASS");
    }

    /**
     * 处理方法级别的 @NPECatch 注解
     * 匹配所有在方法上标注了 @NPECatch 的方法
     */
    @Around("@annotation(npeCatch)")
    public Object handleMethodLevelNPECatch(ProceedingJoinPoint joinPoint, NPECatch npeCatch) throws Throwable {
        return handleNPECatch(joinPoint, npeCatch, "METHOD");
    }

    /**
     * 统一的异常处理逻辑
     */
    private Object handleNPECatch(ProceedingJoinPoint joinPoint, NPECatch npeCatch, String level) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getDeclaringType().getSimpleName() + "." + signature.getName();

        try {
            System.out.println("[" + level + " LEVEL] 开始执行方法: " + methodName);
            return joinPoint.proceed();
        } catch (NullPointerException e) {
            // 输出方法名和异常信息到控制台
            System.err.println("=== NPE异常捕获 ===");
            System.err.println("方法: " + methodName);
            System.err.println("异常信息: " + e.getMessage());
            System.err.println("注解信息: " + (npeCatch.value().isEmpty() ? "无备注" : npeCatch.value()));
            System.err.println("捕获级别: " + level + "级别");
            System.err.println("堆栈跟踪:");
            e.printStackTrace();
            System.err.println("==================");

            // 可以根据需要返回默认值或抛出其他异常
            // return null;
            // 或者重新抛出异常
            throw e;
        } catch (Exception e) {
            // 处理其他异常（可选）
            System.err.println("其他异常 - 方法: " + methodName + ", 异常: " + e.getClass().getSimpleName());
            throw e;
        }
    }
}
